////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Shader extension
//  Copyright (C), Crytek Studios, 2001-2004.
// -------------------------------------------------------------------------
//  File name:   CommonMotionBlurPass.cfi
//  Version:     v1.00
//  Created:     30/01/2006 by Tiago Sousa
//  Compilers:  
//
//	Todo: clean up / generalize sceneMap 
//
// -------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////

// When set motion blur amount is computed based on displacement of previous and current positions
// When not set mesh is still displaced (based on current position) but motion blur amount ignores displacement
// TODO: Should be disabled until properly optimized
//#define HIQ_MOTIONBLUR 1

/// Un-Tweakables ////////////////////// 

sampler2D sceneMapSampler = sampler_state
{
  Texture = $BackBuffer;
  MinFilter = LINEAR
  MagFilter = LINEAR;
  MipFilter = POINT; 
  AddressU = Clamp;
  AddressV = Clamp;	
};

///////////////// vertex input/output //////////////////
struct vert2fragMotionBlur
{
#if _DS || _PS || !VS_TESSELLATION || _RT_NO_TESSELLATION
	OUT_P
#endif

	float4 vVelocity     : TEXCOORDN;
	float4 vVelocityPrev : TEXCOORDN;
	float4 baseTC        : TEXCOORDN;

#if !_PS && !_DS && VS_TESSELLATION && !_RT_NO_TESSELLATION
	float4 vView     : TEXCOORDN;
	half4 vNormal    : TEXCOORDN;
	#if HIQ_MOTIONBLUR
		float4 vViewPrev  : TEXCOORDN;
		half4 vNormalPrev : TEXCOORDN;
		float4x4 matWVP   : MATRIX_WVP;
	#endif
#endif
};

float3x4 MotionBlurData : PI_MotionBlurData;
float4x4 mProjection : PB_ProjMatrix;

float4x4 Matrix_Inst_General_Prev(inout VSVertexContext IN)
{
	float4x4 matInstPrev;

	// Instancing support
#if _RT_INSTANCING_ATTR || _RT_INSTANCING_CONST
	matInstPrev[0] = IN.InstMotionBlurData[0];
	matInstPrev[1] = IN.InstMotionBlurData[1];
	matInstPrev[2] = IN.InstMotionBlurData[2];
	matInstPrev[3] = float4(0, 0, 0, 1);
#else
	matInstPrev[0] = MotionBlurData[0];
	matInstPrev[1] = MotionBlurData[1];
	matInstPrev[2] = MotionBlurData[2];
	matInstPrev[3] = float4(0, 0, 0, 1);
#endif

	// Compose matrixes
	IN.InstMatrix = mul(mProjection, matInstPrev);

	return matInstPrev;
}

// Output previous view space position (If skinning used, position is skinned). Used for motion blur
float4 Pos_Prev_VS_General(float4x4 VPMatrix, inout VSVertexContext IN, out float4x4 matInstPrev)
{
  float4 HPosition;

  // Get instanced matrix
  matInstPrev = Matrix_Inst_General_Prev(IN);
  
  float4x4 MatrixIdentity;
  MatrixIdentity[0] = float4(1,0,0,0);
  MatrixIdentity[1] = float4(0,1,0,0);
  MatrixIdentity[2] = float4(0,0,1,0);
  MatrixIdentity[3] = float4(0,0,0,1);

  HPosition = _pos_Modificators(IN.InstMatrix, MatrixIdentity, IN, true, true, 1.f);//MotionBlurFrameData.w);
   
  return HPosition;
}

///////////////// vertex shaders //////////////////

#if XENON
[maxtempreg(48)]
#endif
vert2fragMotionBlur Common_MotionBlurPassVS(app2vertMotionBlur IN)
{
  vert2fragMotionBlur OUT = (vert2fragMotionBlur)0;
  
  VSVertexContext vertPassPos = (VSVertexContext)0;
  streamPos_FromMotionBlur(IN, vertPassPos);
#ifdef CAFE
	VSVertexContext vertPassPosPrev = vertPassPos;
#else
	VSVertexContext vertPassPosPrev = (VSVertexContext) vertPassPos;
#endif

	float4 HPosition = 0;
	{
		float4 baseTC = vertPassPos.baseTC;
		_TCModify(baseTC, OUT.baseTC, vertPassPos.Position, vertPassPos.ObjToTangentSpace[2], TS_DIFFUSE);

		OUT.baseTC.w = 0;//frac( length( worldPos.xyz + g_VS_WorldViewPos.xyz) ) + 0.01;
		OUT.baseTC.z = 0;
#if %_RT_NEAREST
		// mask out weapons - consider using as gbuffer id also
		OUT.baseTC.z = 1;
#endif

		HPosition = Pos_VS_General(g_VS_ViewProjZeroMatr, vertPassPos);
		OUT.vVelocity = HPosToScreenTC( HPosition );
		vertPassPosPrev.PositionCurr = vertPassPos.Position;
	}
	
	float4x4 matInstPrev;
	{
		float4 HPositionPrev = Pos_Prev_VS_General(g_VS_ViewProjZeroMatr, vertPassPosPrev, matInstPrev);
		OUT.vVelocityPrev = HPosToScreenTC( HPositionPrev );
	}

#if VS_TESSELLATION && !_RT_NO_TESSELLATION
	OUT.vView.xyz = vertPassPos.WorldPos.xyz;
	OUT.vNormal.xyz = normalize(mul((const float3x3)vertPassPos.InstMatrix, vertPassPos.Normal.xyz));
	#if HIQ_MOTIONBLUR
		OUT.vViewPrev.xyz = vertPassPosPrev.PositionPrev.xyz;
		OUT.vNormalPrev.xyz = vertPassPosPrev.Normal.xyz;
		OUT.matWVP = vertPassPosPrev.InstMatrix;
	#endif
#else
	OUT.HPosition = HPosition;
#endif

  float fAlpha = AmbientOp.w;
#if _RT_INSTANCING_ATTR || _RT_INSTANCING_CONST
  {
    float4 InstanceAmbientOp;
    #fetchinst (InstanceAmbientOp = IN.InstAmbientOp, vertPassPos.nInstance);
    fAlpha = InstanceAmbientOp.w;
  }
#endif

	OUT.vVelocity.z = fAlpha;

  return OUT;            
}

///////////////// pixel shaders //////////////////

pixout Common_MotionBlurPassPS(vert2fragMotionBlur IN)
{
	pixout OUT = (pixout)1;  

	float2 vVelocityPrev = IN.vVelocityPrev.xy/IN.vVelocityPrev.w;			// 1 div
	float2 vVelocity = IN.vVelocity.xy/IN.vVelocity.w;									// 1 div
	float2 vVelocityDelta = vVelocityPrev - vVelocity;									// 1 sub

	half fAlpha = IN.vVelocity.z;
#if VS_ALPHABLEND
	// In case alpha blended scale values
	fAlpha *= tex2D(diffuseMapSampler, IN.baseTC.xy).w;	
#endif
	vVelocityDelta *= fAlpha;
	
	OUT.Color.xy = EncodeMotionVector(vVelocityDelta);					
	OUT.Color.zw = float2( IN.baseTC.z, 1 );

#if VS_ALPHATEST
	clip( tex2D(diffuseMapSampler, IN.baseTC).w - 0.5);
#endif
  
	return OUT;
}

#include "CommonMotionBlurPassTess.cfi"

//////////////////////////////// technique ////////////////

technique MotionBlurPass
{
  pass p0
  {
    VertexShader = Common_MotionBlurPassVS() MotionBlurVS;
#ifdef VS_TESSELLATION
		HullShader   = Common_MotionBlurPassHS() GeneralHS;
		DomainShader = Common_MotionBlurPassDS() GeneralDS;
#endif
    PixelShader = Common_MotionBlurPassPS() MotionBlurPS;

    ZEnable = true;
    ZWriteEnable = false;
    CullMode = Back;

#if %HAIR_PASS || %LEAVES
    CullMode = None;
#endif

    ZFunc = LEqual;

		IgnoreMaterialState = true;
  }
}

